package websem.handlers;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import websem.model.InfosTerm;
import websem.model.Paragraph;
import websem.model.Term;

/**
 * Handles operations on documents, such as retrieving the list, add a document..
 * @author Jissay
 */
public class DocumentHandler {

	private static int TITLE_POSITION = -1;
	private static int SUBTITLE_POSITION = -2;
	private static int AUTHOR_POSITION = -3;

	private static String REGEXP = "[A-Za-z]+";
//	private static String REGEXP = "(.)*[a-z\u00E0-\u00FC]+(.)*";
	
	private ArrayList<File> documents_list;
	private ArrayList<Paragraph> paragraphs_list;
	private HashMap<String, Term> terms_list;
	
	private TermHandler termHandler;
	
	public DocumentHandler() {
		this.fillDocumentsList();
		this.fillTermsAndParagraphsList();
		this.termHandler = new TermHandler(this.terms_list);

		double paragraphNumber = new Integer(this.paragraphs_list.size()).doubleValue();
		double averageParagraphSize = this.getAverageParagraphSize(paragraphNumber);
		this.termHandler.fillWeights(paragraphNumber, averageParagraphSize);
	}
	
	public ArrayList<File> getDocuments_list() {
		return documents_list;
	}

	public HashMap<String, Term> getTerms_list() {
		return terms_list;
	}

	public ArrayList<Paragraph> getParagraphs_list() {
		return paragraphs_list;
	}
	
	/**
	 * Get documents list from ressource path
	 * @return
	 */
	protected void fillDocumentsList() {
		this.documents_list= new ArrayList<File>();
		
		// Get documents folder
		File documentsFolder = new File("res/Collection/");
		File[] files_list = documentsFolder.listFiles();
		
		// For each xml document, add it into the returned arraylist
		for (File f : files_list) {
			if (f.isFile()) {
				if (f.getName().endsWith(".xml")) {
					this.documents_list.add(f);
				}
			}
		}
	}
	
	/**
	 * Get terms list from documents in ressource path
	 * @return
	 */
	protected void fillTermsAndParagraphsList() {		
		// Parse docs
		this.terms_list = new HashMap<String, Term>();
		this.paragraphs_list = new ArrayList<Paragraph>();
		
		try {
			
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			
			for (File file : this.documents_list) {
				System.out.println("--- Parsing Doc : " + file.getName());
				Document doc = dBuilder.parse((File)file);
				doc.getDocumentElement().normalize();
				
				// TODO: Refactorer de sorte a ne prendre uniquement que les noeuds titre/sous-titre ensemble
				// puis recuperer les p enfants, pour y ajouter les termes contenus dans les titre/sous-titre
				
				// Get document title, and add his terms to all paragraphs into the document if they match to a 
				// significative term. Store the paragraph list
				
				// Get subtitles and add terms to next paragraphs, in the same node, before any other subtitle.
				// Actualise and store the paragraph list.
				
				this.fillTermsAndParagraphs2(file.getName(), doc);
				
				/*NodeList paragraph_list = doc.getElementsByTagName("P");
				NodeList title_list = doc.getElementsByTagName("TITRE");
				NodeList subtitle_list = doc.getElementsByTagName("SOUS-TITRE");
				NodeList author_list = doc.getElementsByTagName("AUTEUR");
				
				this.fillTermsAndParagraphs(Paragraph.TYPE_PARAGRAPH, paragraph_list, file.getName());
				System.out.println("--- Paragraphs Parsed.");
				this.fillTermsAndParagraphs(Paragraph.TYPE_TITLE, title_list, file.getName());
				System.out.println("--- Title Parsed.");
				this.fillTermsAndParagraphs(Paragraph.TYPE_SUBTITLE, subtitle_list, file.getName());
				System.out.println("--- Subtitle Parsed.");
				this.fillTermsAndParagraphs(Paragraph.TYPE_AUTHOR, author_list, file.getName());
				System.out.println("--- Author Parsed.");*/
				//TODO : Add listes in paragraphs, authors, ...

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	protected void fillTermsAndParagraphs2(String documentName, Document document) {
		HashMap<String, Term> title_terms = new HashMap<String, Term>();
		HashMap<String, Term> subtitle_terms = new HashMap<String, Term>();
		HashMap<String, Term> author_terms = new HashMap<String, Term>();
		this.parseNode(documentName, document, title_terms, subtitle_terms, author_terms);
	}
	
	/**
	 * Parse a node to fill terms and paragraph list
	 * Subtitle terms is only updated when a subtitle is encoutered. The arrayList is cleared when
	 * getting back to an upper level.
	 * @param node
	 * @param title_terms
	 */
	protected HashMap<String, Term> parseNode(String documentName, Node node, HashMap<String, Term> title_terms, HashMap<String, Term> subtitle_terms, HashMap<String, Term> author_terms) {
		String node_name = node.getNodeName();
				
		// Parse PARAGRAPH item
		if (node_name.equals("P")) {
			// Create paragraph
			String xPath = this.getXPath(node, "");
			Paragraph paragraph = new Paragraph(Paragraph.TYPE_PARAGRAPH, xPath, documentName);
			
			// Create terms
			if (node.getTextContent() != null && !node.getTextContent().trim().equals("")) {
				
				// Add title terms
				Set<String> keys = title_terms.keySet();
				for (String k : keys) {
					if (k.length() > 6) { System.out.println("----------------------- BAD KEY"); }
					Term term = this.addPosition(k, paragraph, TITLE_POSITION);
					this.terms_list.put(term.getLabel(), term);
				}
				
				// Add subtitle terms
				keys = subtitle_terms.keySet();
				for (String k : keys) {
					if (k.length() > 6) { System.out.println("----------------------- BAD KEY"); }
					Term term = this.addPosition(k, paragraph, SUBTITLE_POSITION);
					this.terms_list.put(term.getLabel(), term);
				}
				
				// Add author terms
				keys = author_terms.keySet();
				for (String k : keys) {
					if (k.length() > 6) { System.out.println("----------------------- BAD KEY"); }
					Term term = this.addPosition(k, paragraph, AUTHOR_POSITION);
					this.terms_list.put(term.getLabel(), term);
				}
					
				String text = node.getTextContent();
				int index = 0;
				// Delete ponctuation and numbers, and get terms
				String[] terms = text.replaceAll("\\p{Punct}", " ").toLowerCase().split(" ");
				for (String string : terms) {
					index++;
					string = string.replace(" ", "");
					//don t consider terms in stopList or numbers
					if (!TermHandler.isInStopList(string) && string.matches(DocumentHandler.REGEXP)){
						if (string.length() > 1) {
							if (string.length() > 6) {
								string = string.substring(0, 6);
							}
							Term term = this.addPosition(string, paragraph, index);
							this.terms_list.put(string, term);
						}
					}
				}

				// Set word count for paragraph
				paragraph.setCount(index);
				this.paragraphs_list.add(paragraph);
			}
			
		// Parse SUBTITLE item
		} else if (node_name.equals("SOUS-TITRE")) {
			
			String text = node.getTextContent();
			// Delete ponctuation and numbers, and get terms
			String[] terms = text.replaceAll("\\p{Punct}", " ").toLowerCase().split(" ");
			for (String string : terms) {
				string = string.replace(" ", "");
				//don t consider terms in stopList or numbers
				if (!TermHandler.isInStopList(string) && string.matches(DocumentHandler.REGEXP)){
					if (string.length() > 1) {
						if (string.length() > 6) {
							string = string.substring(0, 6);
						}
						Term term = new Term(string);
						subtitle_terms.put(string, term);
					}
				}
			}
			
		// Parse TITLE item
		} else if (node_name.equals("TITRE")) {
			
			String text = node.getTextContent();
			// Delete ponctuation and numbers, and get terms
			String[] terms = text.replaceAll("\\p{Punct}", " ").toLowerCase().split(" ");
			for (String string : terms) {
				string = string.replace(" ", "");
				//don t consider terms in stopList or numbers
				if (!TermHandler.isInStopList(string) && string.matches(DocumentHandler.REGEXP)){
					if (string.length() > 1) {
						if (string.length() > 6) {
							string = string.substring(0, 6);
						}
						Term term = new Term(string);
						title_terms.put(string, term);
					}
				}
			}
		
		// Parse AUTHOR item
		} else if (node_name.equals("AUTEUR")) {
			String text = node.getTextContent();
			// Delete ponctuation and numbers, and get terms
			String[] terms = text.replaceAll("\\p{Punct}", " ").toLowerCase().split(" ");
			for (String string : terms) {
				string = string.replace(" ", "");
				//don t consider terms in stopList or numbers
				if (!TermHandler.isInStopList(string) && string.matches(DocumentHandler.REGEXP)){
					if (string.length() > 1) {
						if (string.length() > 6) {
							string = string.substring(0, 6);
						}
						Term term = new Term(string);
						author_terms.put(string, term);
					}
				}
			}
		} else {
			// If the current node has child, go one level deeper in progression
			// If no child, ignore it
			NodeList node_childs = node.getChildNodes();
			if (node_childs != null && node_childs.getLength() != 0) {
				for (int index = 0; index < node_childs.getLength(); index++) {
					subtitle_terms = this.parseNode(documentName, node_childs.item(index), title_terms, subtitle_terms, author_terms);
				}
				subtitle_terms.clear();
			}
		}
		return subtitle_terms;
	}
	
	/*
	 * Filling terms and paragraphs lists
	 * @param typeParagraph the paragraph type
	 * @param fromList the list to fill
	 * @param fileName the file name
	 * @param paragraphs the document paragraphs nodes
	 
	protected void fillTermsAndParagraphs(String typeParagraph, NodeList fromList, String fileName) {
		for (int i = 0; i < fromList.getLength(); i++) {
			Node node = fromList.item(i);
			
			//Create paragraph
			String xPath = this.getXPath(node, "");
			Paragraph paragraph = new Paragraph(typeParagraph, xPath, fileName);
			
			//Create terms
			if (node.getTextContent() != null && !node.getTextContent().trim().equals("")) {
				
				String text = node.getTextContent();
				int index = 0;
				// Delete ponctuation and numbers, and get terms
				String[] terms = text.replaceAll("\\p{Punct}", " ").toLowerCase().split(" ");
				for (String string : terms) {
					index++;
					string = string.replace(" ", "");
					//don t consider terms in stopList or numbers
					if (!this.isInStopList(string) && string.matches(DocumentHandler.REGEXP)){
						if (string.length() > 1) {
							if (string.length() > 5) {
								string = string.substring(0, 5);
							}
														
							if (typeParagraph.equals(Paragraph.TYPE_TITLE)){
								for (Paragraph p : this.paragraphs_list) {
									// Insert term if same document
									if (p.getDocumentName().equals(fileName)){
										Term term = this.addPosition(string, p, DocumentHandler.TITLE_POSITION);
										this.terms_list.put(string, term);
									}
								}
							} else if (typeParagraph.equals(Paragraph.TYPE_SUBTITLE)){
								Node sibling = node.getNextSibling();
								while (sibling != null){
									if (sibling.getNodeName().equals("P")){
										String path = this.getXPath(sibling, "");
										Paragraph p_new = new Paragraph(Paragraph.TYPE_PARAGRAPH, path, fileName);
										for (Paragraph p_list : this.paragraphs_list) {
											if (p_list.equals(p_new)){
												Term term = this.addPosition(string, p_list, DocumentHandler.SUBTITLE_POSITION);
												this.terms_list.put(string, term);
											}
										}
										sibling = sibling.getNextSibling();
									}
								}
							} else if (typeParagraph.equals(Paragraph.TYPE_PARAGRAPH)){
								Term term = this.addPosition(string, paragraph, DocumentHandler.SUBTITLE_POSITION);
								this.terms_list.put(string, term);
							}
						}
					}
				}

				// Set word count for paragraph
				paragraph.setCount(index);
				this.paragraphs_list.add(paragraph);
			}
		}
	}**/
	
	protected Term addPosition(String string, Paragraph p, int index) {
		Term term = null;
		ArrayList<Integer> positions = new ArrayList<Integer>();
		if (this.terms_list.containsKey(string)) {
			term = this.terms_list.get(string);
			if (term.getInfos().containsKey(p)){
				positions = term.getInfos().get(p).getPositions();
			}
		} else {
			term = new Term(string);
		}
		// Fill infosTerm
		positions.add(index);
		InfosTerm infos = new InfosTerm();
		infos.setPositions(positions);
		term.getInfos().put(p, infos);
		return term;
	}
	
	/**
	 * Get XPath.
	 * @param node
	 * @param string
	 * @return
	 */
	protected String getXPath(Node node, String string){    
		Node parent = node.getParentNode();

	    //Document node
	    if (parent == null) {
	        return string;
	    }
	    
	    //Other nodes
	    NodeList list = parent.getChildNodes();
	    int number = 0;
	    int index = 0;
	    for (int i=0; i<list.getLength(); i++){
	    	Node currentNode = list.item(i);
	    	if (currentNode.getNodeName().equals(node.getNodeName())){
	    		index++;
	    		// Check top level BALADE 
		    	if (currentNode.equals(node) || currentNode.getNodeName().equalsIgnoreCase("BALADE")){
		    		number = index;
		    		break;
		    	}
	    	}
	    }
	    
	    string = "/" + node.getNodeName().toUpperCase() + "[" + String.valueOf(number) + "]" + string;
	    return getXPath(parent, string);
	}

	
	
	/**
	 * Get average paragraph size
	 * @return
	 */
	protected double getAverageParagraphSize(double numberOfParagraph) {
		double sumSize = 0.0;
		for (Paragraph p : this.paragraphs_list) {
			sumSize += p.getCount();
		}
		return sumSize / numberOfParagraph;
	}
}
